* @Title: GtkHeaderBar
* @See_also: #GtkBox
*
- * GtkHeaderBar is similar to a horizontal #GtkBox, it allows
- * to place children at the start or the end. In addition,
- * it allows a title to be displayed. The title will be
- * centered with respect to the width of the box, even if the children
- * at either side take up different amounts of space.
+ * GtkHeaderBar is similar to a horizontal #GtkBox, it allows to place
+ * children at the start or the end. In addition, it allows a title and
+ * subtitle to be displayed. The title will be centered with respect to
+ * the width of the box, even if the children at either side take up
+ * different amounts of space. The height of the titlebar will be
+ * set to provide sufficient space for the subtitle, even if none is
+ * currently set. If a subtitle is not needed, the space reservation
+ * can be turned off with gtk_header_bar_set_has_subtitle().
+ *
+ * GtkHeaderBar can add typical window frame controls, such as minimize,
+ * maximize and close buttons, or the window icon.
*/
#define DEFAULT_SPACING 6
GList *children;
gboolean shows_wm_decorations;
+ gchar *decoration_layout;
+ gboolean decoration_layout_set;
GtkWidget *titlebar_start_box;
GtkWidget *titlebar_end_box;
PROP_CUSTOM_TITLE,
PROP_SPACING,
PROP_SHOW_CLOSE_BUTTON,
+ PROP_DECORATION_LAYOUT,
+ PROP_DECORATION_LAYOUT_SET
};
enum {
_gtk_header_bar_update_window_buttons (GtkHeaderBar *bar)
{
GtkHeaderBarPrivate *priv = gtk_header_bar_get_instance_private (bar);
+ GtkWidget *widget = GTK_WIDGET (bar);
GtkWindow *window;
GtkTextDirection direction;
gchar *layout_desc;
gint i, j;
GMenuModel *menu;
gboolean shown_by_shell;
+ GdkWindowTypeHint type_hint;
- if (!gtk_widget_get_realized (GTK_WIDGET (bar)))
+ if (!gtk_widget_get_realized (widget))
return;
- window = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (bar)));
-
- direction = gtk_widget_get_direction (GTK_WIDGET (window));
-
if (priv->titlebar_icon)
{
gtk_widget_destroy (priv->titlebar_icon);
if (!priv->shows_wm_decorations)
return;
- gtk_widget_style_get (GTK_WIDGET (window),
- "decoration-button-layout", &layout_desc,
- NULL);
+ direction = gtk_widget_get_direction (widget);
+
+ g_object_get (gtk_widget_get_settings (widget),
+ "gtk-shell-shows-app-menu", &shown_by_shell,
+ "gtk-decoration-layout", &layout_desc,
+ NULL);
- g_object_get (gtk_widget_get_settings (GTK_WIDGET (window)),
- "gtk-shell-shows-app-menu", &shown_by_shell, NULL);
+ if (priv->decoration_layout_set)
+ {
+ g_free (layout_desc);
+ layout_desc = g_strdup (priv->decoration_layout);
+ }
+
+ window = GTK_WINDOW (gtk_widget_get_toplevel (widget));
if (!shown_by_shell && gtk_window_get_application (window))
menu = gtk_application_get_app_menu (gtk_window_get_application (window));
else
menu = NULL;
+ type_hint = gtk_window_get_type_hint (window);
+
tokens = g_strsplit (layout_desc, ":", 2);
if (tokens)
{
AtkObject *accessible;
if (strcmp (t[j], "icon") == 0 &&
- gtk_window_get_type_hint (window) == GDK_WINDOW_TYPE_HINT_NORMAL)
+ type_hint == GDK_WINDOW_TYPE_HINT_NORMAL)
{
button = gtk_image_new ();
gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
}
else if (strcmp (t[j], "menu") == 0 &&
menu != NULL &&
- gtk_window_get_type_hint (window) == GDK_WINDOW_TYPE_HINT_NORMAL)
+ type_hint == GDK_WINDOW_TYPE_HINT_NORMAL)
{
button = gtk_menu_button_new ();
gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
gtk_image_set_from_icon_name (GTK_IMAGE (priv->titlebar_icon), "process-stop-symbolic", GTK_ICON_SIZE_MENU);
}
else if (strcmp (t[j], "minimize") == 0 &&
- gtk_window_get_type_hint (window) == GDK_WINDOW_TYPE_HINT_NORMAL)
+ type_hint == GDK_WINDOW_TYPE_HINT_NORMAL)
{
button = gtk_button_new ();
gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
}
else if (strcmp (t[j], "maximize") == 0 &&
gtk_window_get_resizable (window) &&
- gtk_window_get_type_hint (window) == GDK_WINDOW_TYPE_HINT_NORMAL)
+ type_hint == GDK_WINDOW_TYPE_HINT_NORMAL)
{
const gchar *icon_name;
gboolean maximized = _gtk_window_get_maximized (window);
priv->children = NULL;
priv->spacing = DEFAULT_SPACING;
priv->has_subtitle = TRUE;
+ priv->decoration_layout = NULL;
+ priv->decoration_layout_set = FALSE;
init_sizing_box (bar);
construct_label_box (bar);
g_free (priv->title);
g_free (priv->subtitle);
+ g_free (priv->decoration_layout);
G_OBJECT_CLASS (gtk_header_bar_parent_class)->finalize (object);
}
g_value_set_boolean (value, gtk_header_bar_get_has_subtitle (bar));
break;
+ case PROP_DECORATION_LAYOUT:
+ g_value_set_string (value, gtk_header_bar_get_decoration_layout (bar));
+ break;
+
+ case PROP_DECORATION_LAYOUT_SET:
+ g_value_set_boolean (value, priv->decoration_layout_set);
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
gtk_header_bar_set_has_subtitle (bar, g_value_get_boolean (value));
break;
+ case PROP_DECORATION_LAYOUT:
+ gtk_header_bar_set_decoration_layout (bar, g_value_get_string (value));
+ break;
+
+ case PROP_DECORATION_LAYOUT_SET:
+ priv->decoration_layout_set = g_value_get_boolean (value);
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
DEFAULT_SPACING,
GTK_PARAM_READWRITE));
+ /**
+ * GtkHeaderBar:show-close-button:
+ *
+ * Whether to show window decorations.
+ *
+ * Which buttons are actually shown and where is determined
+ * by the #GtkHeaderBar:decoration-layout property, and by
+ * the state of the window (e.g. a close button will not be
+ * shown if the window can't be closed).
+ */
g_object_class_install_property (object_class,
PROP_SHOW_CLOSE_BUTTON,
g_param_spec_boolean ("show-close-button",
FALSE,
GTK_PARAM_READWRITE));
+ /**
+ * GtkHeaderBar:decoration-layout:
+ *
+ * The decoration layout for buttons. If this property is
+ * not set, the #GtkSettings:gtk-decoration-layout setting
+ * is used.
+ *
+ * See gtk_header_bar_set_decoration_layout() for information
+ * about the format of this string.
+ *
+ * Since: 3.12
+ */
+ g_object_class_install_property (object_class,
+ PROP_DECORATION_LAYOUT,
+ g_param_spec_string ("decoration-layout",
+ P_("Decoration Layout"),
+ P_("The layout for window decorations"),
+ NULL,
+ GTK_PARAM_READWRITE));
+
+ /**
+ * GtkHeaderBar:decoration-layout-set:
+ *
+ * Set to %TRUE if #GtkHeaderBar:decoration-layout is set.
+ *
+ * Since: 3.12
+ */
+ g_object_class_install_property (object_class,
+ PROP_DECORATION_LAYOUT_SET,
+ g_param_spec_boolean ("decoration-layout-set",
+ P_("Decoration Layout Set"),
+ P_("Whether the decoration-layout property has been set"),
+ FALSE,
+ GTK_PARAM_READWRITE));
+
/**
* GtkHeaderBar:has-subtitle:
*
return priv->has_subtitle;
}
+
+/**
+ * gtk_header_bar_set_decoration_layout:
+ * @bar: a #GtkHeaderBar
+ * @layout: (allow-none): a decoration layout, or %NULL to
+ * unset the layout
+ *
+ * Sets the decoration layout for this header bar, overriding
+ * the #GtkSettings:gtk-decoration-layout setting.
+ *
+ * There can be valid reasons for overriding the setting, such
+ * as a header bar design that does not allow for buttons to take
+ * room on the right, or only offers room for a single close button.
+ * Split header bars are another example for overriding the
+ * setting.
+ *
+ * The format of the string is button names, separated by commas.
+ * A colon separates the buttons that should appear on the left
+ * from those on the right. Recognized button names are minimize,
+ * maximize, close, icon (the window icon) and menu (a menu button
+ * for the fallback app menu).
+ *
+ * For example, "menu:minimize,maximize,close" specifies a menu
+ * on the left, and minimize, maximize and close buttons on the right.
+ *
+ * Since: 3.12
+ */
+void
+gtk_header_bar_set_decoration_layout (GtkHeaderBar *bar,
+ const gchar *layout)
+{
+ GtkHeaderBarPrivate *priv;
+
+ g_return_if_fail (GTK_IS_HEADER_BAR (bar));
+
+ priv = gtk_header_bar_get_instance_private (bar);
+
+ priv->decoration_layout = g_strdup (layout);
+ priv->decoration_layout_set = (layout != NULL);
+
+ _gtk_header_bar_update_window_buttons (bar);
+
+ g_object_notify (G_OBJECT (bar), "decoration-layout");
+ g_object_notify (G_OBJECT (bar), "decoration-layout-set");
+}
+
+/**
+ * gtk_header_bar_get_decoration_layout:
+ * @bar: a #GtkHeaderBar
+ *
+ * Gets the decoration layout set with
+ * gtk_header_bar_set_decoration_layout().
+ *
+ * Returns: the decoration layout
+ *
+ * Since: 3.12
+ */
+const gchar *
+gtk_header_bar_get_decoration_layout (GtkHeaderBar *bar)
+{
+ GtkHeaderBarPrivate *priv;
+
+ g_return_val_if_fail (GTK_IS_HEADER_BAR (bar), NULL);
+
+ priv = gtk_header_bar_get_instance_private (bar);
+
+ return priv->decoration_layout;
+}
PROP_SHELL_SHOWS_APP_MENU,
PROP_SHELL_SHOWS_MENUBAR,
PROP_SHELL_SHOWS_DESKTOP,
+ PROP_DECORATION_LAYOUT,
PROP_ENABLE_PRIMARY_PASTE,
PROP_RECENT_FILES_ENABLED
};
NULL);
g_assert (result == PROP_SHELL_SHOWS_DESKTOP);
+ /**
+ * GtkSettings:gtk-decoration-layout:
+ *
+ * This setting determines which buttons should be put in the
+ * titlebar of client-side decorated windows, and whether they
+ * should be placed at the left of right.
+ *
+ * The format of the string is button names, separated by commas.
+ * A colon separates the buttons that should appear on the left
+ * from those on the right. Recognized button names are minimize,
+ * maximize, close, icon (the window icon) and menu (a menu button
+ * for the fallback app menu).
+ *
+ * For example, "menu:minimize,maximize,close" specifies a menu
+ * on the left, and minimize, maximize and close buttons on the right.
+ *
+ * Note that buttons will only be shown when they are meaningful.
+ * E.g. a menu button only appears when the desktop shell does not
+ * show the app menu, and a close button only appears on a window
+ * that can be closed.
+ *
+ * Also note that the setting can be overridden with the
+ * #GtkHeaderBar:decoration-layout property.
+ *
+ * Since: 3.12
+ */
+ result = settings_install_property_parser (class,
+ g_param_spec_string ("gtk-decoration-layout",
+ P_("Decoration Layout"),
+ P_("The layout for window decorations"),
+ "menu:close", GTK_PARAM_READWRITE),
+ NULL);
+ g_assert (result == PROP_DECORATION_LAYOUT);
+
/**
* GtkSettings:gtk-enable-primary-paste:
*
static void
on_text_changed (GtkEntry *entry,
GParamSpec *pspec,
- GtkCssProvider *provider)
+ GtkHeaderBar *bar)
{
const gchar *layout;
- gchar *css;
layout = gtk_entry_get_text (entry);
- css = g_strdup_printf ("GtkWindow {\n"
- " -GtkWindow-decoration-button-layout: '%s';\n"
- "}", layout);
-
- gtk_css_provider_load_from_data (provider, css, -1, NULL);
- g_free (css);
+ gtk_header_bar_set_decoration_layout (bar, layout);
}
static void
GtkWidget *check;
GtkBuilder *builder;
GMenuModel *menu;
- GtkCssProvider *provider;
gchar *layout;
g_action_map_add_action (G_ACTION_MAP (gapp), G_ACTION (g_simple_action_new ("test", NULL)));
gtk_header_bar_pack_end (GTK_HEADER_BAR (header), gtk_button_new_with_label ("End"));
gtk_window_set_titlebar (GTK_WINDOW (window), header);
- provider = gtk_css_provider_new ();
-
- gtk_style_context_add_provider_for_screen (gdk_screen_get_default (),
- GTK_STYLE_PROVIDER (provider), 600);
-
grid = gtk_grid_new ();
g_object_set (grid,
"halign", GTK_ALIGN_CENTER,
gtk_widget_set_halign (label, GTK_ALIGN_END);
entry = gtk_entry_new ();
- gtk_widget_style_get (window, "decoration-button-layout", &layout, NULL);
+ g_object_get (gtk_widget_get_settings (window), "gtk-decoration-layout", &layout, NULL);
gtk_entry_set_text (GTK_ENTRY (entry), layout);
g_free (layout);
g_signal_connect (entry, "notify::text",
- G_CALLBACK (on_text_changed), provider);
+ G_CALLBACK (on_text_changed), header);
gtk_grid_attach (GTK_GRID (grid), label, 0, 2, 1, 1);
gtk_grid_attach (GTK_GRID (grid), entry, 1, 2, 1, 1);
- label = gtk_label_new ("Close Button");
+ label = gtk_label_new ("Decorations");
gtk_widget_set_halign (label, GTK_ALIGN_END);
check = gtk_check_button_new ();
g_object_bind_property (header, "show-close-button",